home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr47 / pctuto.zip / DISK3.EXE / lha / CHAP16.DOC < prev    next >
Text File  |  1990-07-20  |  18KB  |  448 lines

  1.  
  2.  
  3.  
  4.                                                                            170
  5.  
  6.                    CHAPTER 16 - LONG SIGNED MULTIPLICATION AND DIVISION
  7.  
  8.  
  9.              Now that you have some subroutines under your belt, it is time to
  10.              get back to multiple word arithmetic. This was put on the back
  11.              burner because we needed to negate long numbers and it is more
  12.              efficient to do that as a subroutine. First, let's negate a long
  13.              number and then put the parts together.
  14.  
  15.              To negate a number you complement it, then add 1. It looks like
  16.              this:
  17.  
  18.                  NUMBER_LENGTH  EQU 4
  19.  
  20.                  variable1 dq   ?
  21.  
  22.                       mov  si, offset variable1
  23.                       mov  cx, NUMBER_LENGTH
  24.                  not_loop:
  25.                       not  WORD PTR [si]
  26.                       add  si, 2
  27.                       loop not_loop
  28.  
  29.                       mov  si, offset variable1
  30.                       mov  cx, NUMBER_LENGTH
  31.                       stc                      ; set carry flag
  32.                  add_loop:
  33.                       adc  WORD PTR [si], 0
  34.                       inc  si
  35.                       inc  si
  36.                       loop add_loop
  37.  
  38.              This is straightforward. First negate, then add 1. The first add
  39.              will add 1 because the carry flag is set. If there is a carry
  40.              out, it will be taken care of in the next word with ADC (we add
  41.              nothing but the carry). We can make this more compact and
  42.              efficient with:
  43.  
  44.                       mov  si, offset variable1
  45.                       mov  cx, NUMBER_LENGTH
  46.                       stc                      ; set carry flag
  47.                  negate_loop:
  48.                       not  WORD PTR [si]
  49.                       adc  WORD PTR [si], 0
  50.                       inc  si
  51.                       inc  si
  52.                       loop negate_loop
  53.  
  54.              Neither NOT nor INC effect CF, the carry flag, so the correct CF
  55.              value will be propagated through the whole long number. 
  56.  
  57.  
  58.              When we do negation during our multiplication, the multiplicand
  59.              will be a 4 word negation while the result will be a 5 word
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              Chapter 16 - Multiple Word Arithmetic III                     171
  69.              _________________________________________
  70.  
  71.              negation, so we will pass the length as a parameter. The call in
  72.              C would look like this:
  73.  
  74.                  negate_it ( &number, length ) ; {1}
  75.  
  76.              On entry, the stack will look like this:
  77.  
  78.                            length    bp + 6
  79.                            address   bp + 4
  80.                            old IP    bp + 2
  81.                  bp   ->   old BP    bp + 0
  82.  
  83.              Here is the entire subroutine:
  84.  
  85.              ; - - - - - START SUBROUTINES BELOW THIS LINE
  86.              negate_it proc near
  87.  
  88.                  NUMBER_LENGTH  EQU [bp+6]
  89.                  NUMBER_ADDRESS EQU [bp+4]
  90.  
  91.                  push bp
  92.                  mov  bp, sp
  93.                  PUSHREGS cx, si
  94.  
  95.  
  96.                  mov  si, NUMBER_ADDRESS
  97.                  mov  cx, NUMBER_LENGTH
  98.                  stc                      ; set carry flag
  99.              negate_loop:
  100.                  not  WORD PTR [si]
  101.                  adc  WORD PTR [si], 0
  102.                  inc  si
  103.                  inc  si
  104.                  loop negate_loop
  105.  
  106.                  POPREGS cx, si
  107.                  pop  bp
  108.                  ret                 ; calling routine adjusts stack
  109.  
  110.              negate_it endp
  111.  
  112.              ; - - - - - END SUBROUTINES ABOVE THIS LINE
  113.  
  114.              Well, so far we have the negation routine and the unsigned
  115.              multiplication and division routines. What else is necessary?
  116.              Only the main program, and here it is for multiplication:
  117.  
  118.              ; - - - - - START DATA BELOW THIS LINE
  119.              multiplicand       dq   ?
  120.              multiplier         dw   ?
  121.              result             dt   ?
  122.              result_sign_flag   db   ?
  123.              ; - - - - - END DATA ABOVE THIS LINE
  124.  
  125.              ____________________
  126.  
  127.                 1. For you non-C people, the '&' stands for the address.
  128.  
  129.  
  130.  
  131.  
  132.              The PC Assembler Tutor                                        172
  133.              ______________________
  134.  
  135.              ; - - - - - START CODE BELOW THIS LINE
  136.              outer_loop:
  137.                  mov  result_sign_flag, 0      ; assume positive
  138.                  mov  ax, offset multiplicand
  139.                  call get_signed_8byte
  140.  
  141.                  test WORD PTR multiplicand + 6, 8000h   ; is it negative ?
  142.                  jz   get_next_number
  143.  
  144.                  mov  ax,4                     ; negate 4 word number
  145.                  push ax
  146.                  mov  ax, offset multiplicand
  147.                  push ax
  148.                  call negate_it
  149.                  add  sp, 4                    ; clear 2 pushes off stack
  150.                  not  result_sign_flag         ; reverse sign of result
  151.  
  152.              get_next_number:
  153.                  call get_signed               ; get signed multiplier
  154.                  mov  multiplier, ax
  155.                  test ax, 8000h                ; is it negative
  156.                  jz   do_the_multiplication
  157.  
  158.                  neg  multiplier               ; negate
  159.                  not  result_sign_flag         ; reverse sign of result
  160.  
  161.              do_the_multiplication:
  162.                  mov  ax, offset result
  163.                  push ax
  164.                  mov  ax, multiplier      ; the number, not the address
  165.                  push ax
  166.                  mov  ax, offset multiplicand
  167.                  push ax
  168.                  call multiply_it
  169.                  add  sp, 6                    ; clear 3 pushes off stack
  170.  
  171.                  ; is the result negative?
  172.                  test result_sign_flag, 0FFh   ; 1111 1111 mask
  173.                  jz   print_it
  174.  
  175.                  mov  ax, 5                    ; 5 word result
  176.                  push ax
  177.                  mov  ax, offset result
  178.                  push ax
  179.                  call negate_it
  180.                  add  sp, 4                    ; clear 2 pushes off stack
  181.  
  182.              print_it:
  183.                  mov  ax, WORD PTR result + 8  ; top two bytes
  184.                  call print_hex
  185.                  mov  ax, offset result        ; the rest of result
  186.                  call print_signed_8byte
  187.  
  188.                  jmp  outer_loop
  189.              ; - - - - - END CODE ABOVE THIS LINE
  190.  
  191.              The driver routine gets an 8 byte signed number. If the number is
  192.  
  193.  
  194.  
  195.  
  196.              Chapter 16 - Multiple Word Arithmetic III                     173
  197.              _________________________________________
  198.  
  199.              negative it negates the number (to make it positive) and switches
  200.              the sign of the result_sign_flag. The sign flag will either be
  201.              00h for positive or FFh for negative. It then gets a two byte
  202.              signed number. If it is negative, the routine negates it and
  203.              switches the sign flag. At this point both numbers are positive,
  204.              so it calls the unsigned multiplication routine. At the end, it
  205.              checks the result_sign_flag to see if the result should be
  206.              positive or negative. If it should be negative, the routine calls
  207.              negate_it one more time. Finally, the routine prints the number.
  208.              The hex portion will be 0000 for positive or FFFF for negative
  209.              unless the value is larger than an 8 byte signed number can hold,
  210.              at which point the value of the 8 byte signed number will be
  211.              incorrect.
  212.  
  213.              Here's the unsigned multiplication routine which has been turned
  214.              into a subroutine:
  215.  
  216.              ; - - - - - 
  217.              multiply_it proc near
  218.  
  219.                  RESULT_ADDRESS           EQU  [bp+8]
  220.                  MULTIPLIER_VALUE         EQU  [bp+6]
  221.                  MULTIPLICAND_ADDRESS     EQU  [bp+4]
  222.  
  223.                  push bp
  224.                  mov  bp, sp
  225.                  PUSHREGS  ax, bx, cx, dx, si, di
  226.  
  227.                  mov  si, MULTIPLICAND_ADDRESS ; load pointers
  228.                  mov  bx, RESULT_ADDRESS
  229.  
  230.                  mov  cx, 4          ; number of words
  231.                  sub  di,di          ; clear di 
  232.  
  233.              mult_loop:
  234.                  mov  ax, [si]       ; multiplicand to ax
  235.                  mul  WORD PTR  MULTIPLIER_VALUE
  236.                  add  ax, di         ; add high word from last multiplication
  237.                  jnc  store_result
  238.                  inc  dx
  239.              store_result:
  240.                  mov  [bx], ax       ; store 1 word of result.
  241.                  mov  di, dx         ; save high word for next multiplication
  242.                  add  si, 2          ; increment pointers
  243.                  add  bx, 2
  244.                  loop mult_loop
  245.  
  246.                  mov  [bx], di       ; move last word of result
  247.  
  248.                  POPREGS   ax, bx, cx, dx, si, di
  249.                  pop  bp
  250.                  ret                 ; calling routine adjusts stack
  251.  
  252.              multiply_it endp
  253.              ; - - - - - - - - - - - 
  254.  
  255.              Draw a picture of the stack to verify that the EQU values are
  256.  
  257.  
  258.  
  259.  
  260.              The PC Assembler Tutor                                        174
  261.              ______________________
  262.  
  263.              correct. The multiplication and the negation subroutines go in
  264.              the subroutine section of SUBTEMP1.ASM. The driver routine is the
  265.              main routine. If you don't remember how this multiplication
  266.              routine works, go back to the chapter on unsigned multiple word
  267.              multiplication since the code is the same.
  268.  
  269.  
  270.  
  271.              DIVISION
  272.  
  273.              Division is the same situation. We need a driver routine, but the
  274.              division itself will be the unsigned division. In division, the
  275.              remainder is the same sign as the dividend, and the sign of the
  276.              quotient is (dividend_sign XOR divisor_sign). If both signs are
  277.              the same, the quotient is positive; if the signs are different
  278.              the quotient is negative. Here's the driver routine:
  279.  
  280.              ; - - - - - START DATA BELOW THIS LINE 
  281.              dividend                dq  ? 
  282.              divisor                 dw  ? 
  283.              quotient                dq  ? 
  284.              remainder               dw  ? 
  285.              quotient_sign_flag      db  ? 
  286.              remainder_sign_flag     db  ? 
  287.              ; - - - - - END DATA ABOVE THIS LINE 
  288.  
  289.              ; - - - - - START CODE BELOW THIS LINE 
  290.              outer_loop: 
  291.                     mov   quotient_sign_flag, 0            ; assume positive
  292.                     mov   remainder_sign_flag, 0 
  293.  
  294.                     mov   ax, offset dividend 
  295.                     call  get_signed_8byte 
  296.                     test  WORD PTR (dividend + 6), 8000h   ; is it negative?
  297.                     jz    get_next_number 
  298.               
  299.                     mov   ax,4                      ; negate 4 word number 
  300.                     push  ax 
  301.                     mov   ax, offset dividend 
  302.                     push  ax 
  303.                     call  negate_it 
  304.                     add   sp, 4                     ; adjust stack 
  305.                     not   quotient_sign_flag        ; switch sign of quotient
  306.                     mov   remainder_sign_flag, 0FFh ; remainder is negative 
  307.               
  308.              get_next_number: 
  309.                     call  get_signed 
  310.                     mov   divisor, ax 
  311.                     test  ax, 8000h                 ; is it negative 
  312.                     jz    do_the_division 
  313.               
  314.                     neg   divisor 
  315.                     not   quotient_sign_flag       ; switch sign of quotient
  316.               
  317.              do_the_division: 
  318.                     mov   ax, offset remainder 
  319.                     push  ax 
  320.  
  321.  
  322.  
  323.  
  324.              Chapter 16 - Multiple Word Arithmetic III                     175
  325.              _________________________________________
  326.  
  327.                     mov   ax, offset quotient 
  328.                     push  ax 
  329.                     mov   ax, divisor        ; the number, not the address
  330.                     push  ax 
  331.                     mov   ax, offset dividend    
  332.                     push  ax 
  333.                     call  divide_it 
  334.                     add   sp, 8                    ; clear 4 pushes off stack
  335.               
  336.                     ; are the remainder and quotient negative? 
  337.                     test  remainder_sign_flag, 0FFh 
  338.                     jz    test_the_quotient 
  339.                     neg   remainder 
  340.               
  341.              test_the_quotient: 
  342.                     test  quotient_sign_flag, 0FFh      ; 1111 1111 mask 
  343.                     jz    print_it 
  344.                     
  345.                     mov   ax, 4                         ; 4 word result 
  346.                     push  ax 
  347.                     mov   ax, offset quotient 
  348.                     push  ax 
  349.                     call  negate_it 
  350.                     add   sp, 4                   ; clear 2 pushes off stack
  351.               
  352.              print_it: 
  353.                     mov   ax, offset quotient 
  354.                     call  print_signed_8byte 
  355.                     mov   ax, remainder 
  356.                     call  print_signed 
  357.               
  358.                     jmp  outer_loop 
  359.              ; - - - - - END CODE ABOVE THIS LINE 
  360.  
  361.              We get the dividend and check the sign. If it is negative, we (1)
  362.              negate the number, (2) switch the sign of the quotient, and (3)
  363.              set the remainder sign flag to negative. We get the divisor,
  364.              check for negative; if it is negative we negate it and switch the
  365.              sign of the quotient. We now have two unsigned numbers and do
  366.              unsigned division. After division, both the quotient and
  367.              remainder are adjusted for sign.
  368.  
  369.              The division routine is the same as the unsigned routine before
  370.              except it is now a subroutine:
  371.  
  372.              ; - - - - - - - - ENTER SUBROUTINE BELOW THIS LINE 
  373.              divide_it proc near 
  374.               
  375.                     REMAINDER_ADDRESS         EQU  [bp+10] 
  376.                     QUOTIENT_ADDRESS          EQU  [bp+8] 
  377.                     DIVISOR_VALUE             EQU  [bp+6] 
  378.                     DIVIDEND_ADDRESS          EQU  [bp+4] 
  379.               
  380.                     push  bp 
  381.                     mov   bp, sp 
  382.                     PUSHREGS  ax, bx, cx, dx, si, di 
  383.               
  384.  
  385.  
  386.  
  387.  
  388.              The PC Assembler Tutor                                        176
  389.              ______________________
  390.  
  391.               
  392.                     mov   si, DIVIDEND_ADDRESS  
  393.                     mov   bx, QUOTIENT_ADDRESS 
  394.                     add   si, 6                 ; start at the top word
  395.                     add   bx, 6 
  396.                     mov   di, WORD PTR  DIVISOR_VALUE 
  397.                     mov   cx, 4                    ; number of words 
  398.                     sub   dx, dx          ; clear dx for first division 
  399.               
  400.              division_loop: 
  401.                     mov   ax, [si]        ; dividend word to ax 
  402.                     div   di  
  403.                     mov   [bx], ax        ; word of result to quotient 
  404.                     sub   si, 2           ; decrement the pointers 
  405.                     sub   bx, 2 
  406.                     loop  division_loop 
  407.               
  408.                     mov   bx, REMAINDER_ADDRESS    ; store remainder
  409.                     mov   [bx], dx 
  410.               
  411.                     POPREGS  ax, bx, cx, dx, si, di 
  412.                     pop   bp 
  413.                     ret                ; calling routine adjusts the stack 
  414.               
  415.              divide_it  endp 
  416.               
  417.              ; - - - - - - - - ENTER SUBROUTINE ABOVE THIS LINE 
  418.  
  419.              Draw a picture of the stack to verify that the EQU statements are
  420.              correct for a NEAR routine. The division and negation subroutines
  421.              go in the SUBROUTINES section of SUBTEMP1.ASM. The driver is the
  422.              main program. If you don't remember how this division works, go
  423.              back to the division chapter and look it over. Try out a few
  424.              numbers to make sure that it is working the way it should.
  425.  
  426.  
  427.              DATA INTEGRITY
  428.  
  429.              One thing that may have been annoying some of you is that when
  430.              the programs sent us numbers for multiplication and division we
  431.              sometimes negated them, effectively changing the data in memory,
  432.              but never changed them back when we were done. In an operational
  433.              subroutine, you would have to do it differently. The logic would
  434.              be:
  435.  
  436.                  NUMBER NEGATIVE?    no   everything's o.k.
  437.  
  438.                       yes
  439.  
  440.                       make copy
  441.                       negate
  442.                       reset pointer
  443.  
  444.              If the number is positive we won't change it. If the number is
  445.              negative, we make a copy, negate the copy and use the copy for
  446.              the operation.
  447.  
  448.